package util.hibernate;

import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Disjunction;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.ProjectionList;
import org.hibernate.criterion.Projections;

import org.hibernate.criterion.Restrictions;
import org.hibernate.criterion.Subqueries;
import org.hibernate.sql.JoinType;

public abstract class GenericDAO extends HibernateUtil {
	private Session session;
	private Transaction tx;
	// HibernateConfig HibernateFactory;
	private Criteria criteria;
	private DetachedCriteria detachedCriteria;

	private Class<?> clazz;
	private int idAfterInsert;

	public GenericDAO() {
		// HibernateFactory = new HibernateConfig("hibernate.cfg.xml");
		super();
	}

	public void addFilter(String operator, String field, Object value) {
		if (operator.equals("like"))
			criteria.add(Restrictions.like(field, value));
		if (operator.equals("ne"))
			criteria.add(Restrictions.ne(field, value));
		if (operator.equals("eq"))
			criteria.add(Restrictions.eq(field, value));
	}

	public List<?> query(String hql) {
		startOperation();
		Query qry = session.createQuery(hql);
		List<?> list = qry.list();
		return list;
	}

	public void addFilter(String operator, String field, String value) {
		if (operator.equals("like"))
			criteria.add(Restrictions.like(field, value));
		if (operator.equals("ne"))
			criteria.add(Restrictions.ne(field, value));
		if (operator.equals("eq"))
			criteria.add(Restrictions.eq(field, value));
		if (operator.equals("and"))
			criteria.add(Restrictions.eq(field, value));

	}

	public void addCollectionFilter(String operator, String field, List<?> value) {

		if (operator.equals("in"))
			criteria.add(Restrictions.in(field, value));

	}

	public void searchFilter(String operator, String field, String value) {

		// String s = "nip,nama,inisial";

		String[] arrayField = field.split(",");

		Disjunction disjunction = Restrictions.disjunction();
		for (int i = 0; i < arrayField.length; i++) {
			// System.out.println(i + " " + arrayField[i]);
			if (operator.equals("like")) {
				Criterion or = Restrictions.like(arrayField[i], "%" + value
						+ "%");
				disjunction.add(or);
			}
			if (operator.equals("ne")) {
				Criterion or = Restrictions.ne(arrayField[i], value);
				disjunction.add(or);
			}
			if (operator.equals("eq")) {
				Criterion or = Restrictions.eq(arrayField[i], value);
				disjunction.add(or);
			}

		}
		criteria.add(disjunction);
		// criteria.add(Restrictions.disjunction().adddisjunction);
	}

	public void addOrder(String sord, String sidx) {
		if (sord.equals("asc")) {
			criteria.addOrder(Order.asc(sidx));
		} else {
			criteria.addOrder(Order.desc(sidx));
		}
	}

	protected Long count() {
		long count = 0;
		try {
			startOperation();
			Criteria crit = session.createCriteria(getClazz());
			crit.setProjection(Projections.rowCount());
			count = ((Long) crit.list().get(0)).longValue();
			tx.commit();
		} catch (HibernateException e) {
			handleException(e);
		} finally {
			session.close();
		}
		return count;
	}

	protected void createAlias(String from, String to) {
		// criteria.createAlias(from, to);
		criteria.createAlias(from, to);
	}

	protected void createDetachedCriteria() {
		detachedCriteria = DetachedCriteria.forClass(getClazz());
	}

	protected void createDetachedAlias(String from, String to) {
		detachedCriteria.createAlias(from, to);
	}

	protected void createAliasLeftJoin(String from, String to) {
		// criteria.createAlias(from, to);
		criteria.createAlias(from, to, JoinType.LEFT_OUTER_JOIN);
	}

	protected void createAliasRightJoin(String from, String to) {
		// criteria.createAlias(from, to);
		criteria.createAlias(from, to, JoinType.RIGHT_OUTER_JOIN);
	}

	protected void createCriteria() {
		session = getSessionFactory().openSession();
		criteria = session.createCriteria(getClazz());
		// criteria.setFetchMode(getClass().getName(), FetchMode.JOIN);
	}

	protected void groupByLastId(String groupProperty, String field) {
		ProjectionList p = Projections.projectionList();
		p.add(Projections.groupProperty(groupProperty));
		p.add(Projections.max(field));
		detachedCriteria.setProjection(p);
		criteria.add(Subqueries.propertiesIn(new String[] { groupProperty,
				field }, detachedCriteria));
	}

	protected void addDetachedFilter() {
		criteria.add(Subqueries.propertiesIn(new String[] {
				"strukturJabatan.id", "id" }, detachedCriteria));
	}

	protected void delete(int id) {
		Object obj = null;
		try {
			startOperation();
			obj = session.load(getClazz(), id);
			session.delete(obj);
			tx.commit();
		} catch (HibernateException e) {
			handleException(e);
		} finally {
			session.close();
		}
	}

	protected void delete(Object object) {
		try {
			tx = session.beginTransaction();
			session.delete(object);
			tx.commit();
		} catch (HibernateException e) {
			handleException(e);
		} finally {
			session.close();
		}
	}

	protected void deleteAll() {
		session.createSQLQuery("truncate table MyTable").executeUpdate();
	}

	protected void deleteAll(List<?> object) {

		try {
			startOperation();
			for (Object obj : object) {
				session.delete(obj);
			}
			tx.commit();
		} catch (HibernateException e) {
			handleException(e);
		} finally {
			// session.close();
		}
	}

	protected List<?> find() {
		List<?> obj = null;
		try {
			tx = session.beginTransaction();
			obj = criteria.list();
			tx.commit();
		} catch (HibernateException e) {
			handleException(e);
		} finally {
			session.close();
		}
		return (List<?>) obj;
	}

	protected Object find(int id) {
		Object obj = null;
		try {
			startOperation();
			obj = session.load(getClazz(), id);
			tx.commit();
		} catch (HibernateException e) {
			handleException(e);
		} finally {
			// session.close();
		}
		return obj;
	}

	protected List<?> find(int from, int to) {
		List<?> obj = null;
		try {
			tx = session.beginTransaction();
			criteria.setFirstResult(from).setMaxResults(to);
			obj = criteria.list();
			tx.commit();
		} catch (HibernateException e) {
			handleException(e);
		} finally {
			session.close();
		}
		return (List<?>) obj;
	}

	protected List<?> findAll(Class<?> clazz) {
		List<?> objects = null;
		try {
			startOperation();
			Query query = session.createQuery("from " + clazz.getName());
			objects = query.list();
			tx.commit();
		} catch (HibernateException e) {
			handleException(e);
		} finally {
			session.close();
		}
		return objects;
	}

	protected Object findOne(Class<?> clazz) {
		Object objects = null;
		try {
			startOperation();
			Query query = session.createQuery("from " + clazz.getName());
			objects = query.uniqueResult();
			tx.commit();
		} catch (HibernateException e) {
			handleException(e);
		} finally {
			session.close();
		}
		return objects;
	}

	protected Object findUnique() {
		Object obj = null;
		try {
			tx = session.beginTransaction();
			obj = criteria.uniqueResult();
			tx.commit();
		} catch (HibernateException e) {
			handleException(e);
		} finally {
			session.close();
		}
		return obj;
	}

	protected void flushSession() {
		session.flush();
	}

	public Class<?> getClazz() {
		return clazz;
	}

	protected void handleException(HibernateException e) {
		tx.rollback();
		throw new DataAccessLayerException(e);
	}

	protected Object insert(Object obj) {

		try {
			startOperation();
			session.save(obj);
			tx.commit();
		} catch (HibernateException e) {
			handleException(e);
		} finally {
			session.close();
		}
		return obj;
	}

	protected Object lastId() {
		Object obj = null;
		try {
			tx = session.beginTransaction();
			criteria.addOrder(Order.desc("id"));
			criteria.setMaxResults(1);
			obj = criteria.uniqueResult();
			tx.commit();
		} catch (HibernateException e) {
			handleException(e);
		} finally {
			session.close();
		}
		return obj;
	}

	public void setClazz(Class<?> clazz) {
		this.clazz = clazz;
	}

	protected void startOperation() throws HibernateException {

		session = getSessionFactory().openSession();
		tx = session.beginTransaction();
	}

	protected void update(Object obj) {

		try {
			tx = session.beginTransaction();
			session.update(obj);
			tx.commit();
		} catch (HibernateException e) {
			handleException(e);
		} finally {
			session.close();
		}
	}

	protected void updateOne(Object obj) {

		try {
			startOperation();
			session.update(obj);
			tx.commit();
		} catch (HibernateException e) {
			handleException(e);
		} finally {
			session.close();
		}
	}

	public int getIdAfterInsert() {
		return idAfterInsert;
	}

	public void setIdAfterInsert(int idAfterInsert) {
		this.idAfterInsert = idAfterInsert;
	}

	public DetachedCriteria getDetachedCriteria() {
		return detachedCriteria;
	}

	public void setDetachedCriteria(DetachedCriteria detachedCriteria) {
		this.detachedCriteria = detachedCriteria;
	}

}